package com.opensns.android;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnPerRoute;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.scheme.SocketFactory;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

import android.content.Context;
import android.content.SharedPreferences;

public class Utility {
	
	/**
	 * 计算文本的信息摘要。
	 * @param text 文本字符串。
	 * @param encoding 文本的编码，如 UTF-8 等。
	 * @param algorithm 信息摘要算法。
	 * @return 信息摘要字符串。
	 */
	public static String getMessageDigest(String text, String encoding, String algorithm) {
		try {
			MessageDigest md = MessageDigest.getInstance(algorithm);
			StringBuffer result = new StringBuffer();
			byte[] data = null;
			try {
				data = md.digest(text.getBytes(algorithm));
			} catch (UnsupportedEncodingException e) {
				data = md.digest(text.getBytes());				
			}
			for (byte b : data) {
	            result.append(String.format("%02x", b));
	        }
			return result.toString();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 拼接带查询参数的 URL。
	 * @param url 基本的URL。
	 * @param params 查询参数。
	 * @return 带查询参数的 URL。
	 */
	public static String buildUrl(String url, Parameters params) {
		StringBuilder sb = new StringBuilder(url);
		if (params != null) {
			boolean first = true;
	        for (int i = 0; i < params.size(); i++) {
	            if (first) {
	            	first = false;
	            	sb.append("?");
	            } else {
	            	sb.append("&");
	            }
	            sb.append(URLEncoder.encode(params.getKey(i)));
	            sb.append("=");
	            sb.append(URLEncoder.encode(params.getValue(i)));
	        }
		}
        return sb.toString();
	}
	
	/**
	 * 解析 URL 所带的查询参数。
	 * @param url 被解析的 URL。
	 * @return URL 中所带查询参数。 
	 */
	public static Parameters parseUrl(String url) {
		Parameters params = new Parameters();
		try {
			URL u = new URL(url);
			String query = u.getQuery();
			if (query != null) {
	            String array[] = query.split("&");
	            for (String param : array) {
	                String v[] = param.split("=");
	                params.add(URLDecoder.decode(v[0]), URLDecoder.decode(v[1]));
	            }
	        }
		} catch (Exception e) {
			
		}
		return params;
	}
	
	/**
	 * 发送一个 HTTP GET 请求从开放平台获取数据。
	 * @param context 调用方法的上下文。
	 * @param url 请求的 URL。
	 * @param params 请求的参数。
	 * @param handler 异常处理回调。
	 * @return 请求响应的数据。
	 * @throws OpenSnsException 开放平台返回的异常信息。
	 * @throws Exception 程序异常。
	 */
	public static String requestGet(Context context, String url, Parameters params, OpenSnsExceptionHandler handler) 
			throws OpenSnsException, Exception {
		
		String result = null;
		url = buildUrl(url, params);
		HttpGet request = new HttpGet(url);
		HttpResponse response = HttpClientManager.getHttpClient().execute(request);
		StatusLine status = response.getStatusLine();
		int statusCode = status.getStatusCode();
		result = EntityUtils.toString(response.getEntity());
		if(statusCode != 200) {
			OpenSnsException ex = handler.parseException(result, statusCode);
			throw ex;
		}
		return result;
	}
	
	/**
	 * 发送一个 HTTP POST 请求从开放平台获取数据。
	 * @param context 调用方法的上下文。
	 * @param url 请求的 URL。
	 * @param params 请求的参数。
	 * @param handler 异常处理回调。
	 * @return 请求响应的数据。
	 * @throws OpenSnsException 开放平台返回的异常信息。
	 * @throws Exception 程序异常。
	 */
	public static String requestPost(Context context, String url, Parameters params, OpenSnsExceptionHandler handler) 
			throws OpenSnsException, Exception {
		
		String result = null;
		HttpPost request = new HttpPost(url);
		List<NameValuePair> data = new ArrayList<NameValuePair>();
		int count = params.size();
		for (int i = 0; i < count; i++) {
			data.add(new BasicNameValuePair(params.getKey(i), params.getValue(i)));
		}
		request.setEntity(new UrlEncodedFormEntity(data, HTTP.UTF_8));
		HttpResponse response = HttpClientManager.getHttpClient().execute(request);
		StatusLine status = response.getStatusLine();
		int statusCode = status.getStatusCode();
		result = EntityUtils.toString(response.getEntity());
		if(statusCode != 200) {
			OpenSnsException ex = handler.parseException(result, statusCode);
			throw ex;
		}
		return result;
	}
	
	/**
	 * 将 Access Token 保存在 SharePreferences 中。
	 * @param context 调用方法的上下文。
	 * @param token 要保存的 Access Token。
	 */
	public static void storeAccessToken(Context context, OauthAccessToken token) {
		SharedPreferences p = context.getSharedPreferences(token.platform, Context.MODE_PRIVATE);
		SharedPreferences.Editor ed = p.edit();
		ed.putString("access_token", token.accessToken);
		ed.putString("refresh_token", token.refreshToken);
		ed.putString("uid", token.uid);
		ed.putString("scope", token.scope);
		ed.putLong("expires", token.expiresIn);
		ed.commit();
	}
	
	/**
	 * 获取保存在 SharePreferences 中的 Access Token。
	 * @param context 调用方法的上下文。
	 * @param platform Access Token 对应的开发平台。
	 * @return 获得的 Access Token。
	 */
	public static OauthAccessToken restoreAccessToken(Context context, String platform) {
		SharedPreferences p = context.getSharedPreferences(platform, Context.MODE_PRIVATE);
		OauthAccessToken token = new OauthAccessToken(platform);
		token.accessToken = p.getString("access_token", null);
		token.refreshToken = p.getString("refresh_token", null);
		token.uid = p.getString("uid", null);
		token.scope = p.getString("scope", null);
		token.expiresIn = p.getLong("expires", 0);
		return (token.accessToken != null) ? token : null;
	}

}

class HttpClientManager {
	
	private static final int CONNECTION_TIMEOUT = 50000;
    private static final int SOCKET_TIMEOUT = 200000;
    private static final int SOCKET_BUFFER_SIZE = 8192;
	private static final DefaultHttpClient mClient;
	
	static {

        // Set basic data
        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, "UTF-8");
        HttpProtocolParams.setUseExpectContinue(params, true);
        HttpProtocolParams.setUserAgent(params, "android-webkit");

        // Make pool
        ConnPerRoute connPerRoute = new ConnPerRouteBean(12);
        ConnManagerParams.setMaxConnectionsPerRoute(params, connPerRoute);
        ConnManagerParams.setMaxTotalConnections(params, 20);

        // Set timeout
        HttpConnectionParams.setStaleCheckingEnabled(params, false);
        HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
        HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT);
        HttpConnectionParams.setSocketBufferSize(params, SOCKET_BUFFER_SIZE);

        // Some client params
        HttpClientParams.setRedirecting(params, false);

        // Register https shemas!
        SchemeRegistry reg = new SchemeRegistry();
        try {        	
            reg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
            reg.register(new Scheme("https", FullTrustSSLSocketFactory.getDefault(), 443));
		} catch (Exception e) {

		}
        
        ClientConnectionManager conman = new ThreadSafeClientConnManager(params, reg);
        mClient = new DefaultHttpClient(conman, params);
	}
	
	private HttpClientManager() {
		
    }

    public static HttpClient getHttpClient() {
    	return mClient;
    }

    public static synchronized CookieStore getCookieStore() {
    	return mClient.getCookieStore();
    }
    
}

class FullTrustManager implements X509TrustManager {
	
    public void checkClientTrusted(X509Certificate[] cert, String authType) 
    		throws CertificateException {
    	
    }

    public void checkServerTrusted(X509Certificate[] cert, String authType) 
    		throws CertificateException {
    	
    }

    public X509Certificate[] getAcceptedIssuers() {
    	return null;
    }
}

class FullTrustSSLSocketFactory extends SSLSocketFactory {
	
	private javax.net.ssl.SSLSocketFactory factory;
	
	public FullTrustSSLSocketFactory() 
			throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
		super(null);
		try {
			SSLContext sslcontext = SSLContext.getInstance("TLS");
			sslcontext.init(null, new TrustManager[] { new FullTrustManager() }, null);
			factory = sslcontext.getSocketFactory();
			setHostnameVerifier(new AllowAllHostnameVerifier());
		} catch (Exception ex) {
			
		}
	}
	
	public static SocketFactory getDefault() 
			throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
		return new FullTrustSSLSocketFactory();
	}
	
	@Override
    public Socket createSocket() throws IOException {
		return factory.createSocket();
    }
	
	@Override
    public Socket createSocket(Socket socket, String s, int i, boolean flag) throws IOException {
		return factory.createSocket(socket, s, i, flag);
    }

    public Socket createSocket(InetAddress inaddr, int i, InetAddress inaddr1, int j) throws IOException {
    	return factory.createSocket(inaddr, i, inaddr1, j);
    }

    public Socket createSocket(InetAddress inaddr, int i) throws IOException {
    	return factory.createSocket(inaddr, i);
    }

    public Socket createSocket(String s, int i, InetAddress inaddr, int j) throws IOException {
    	return factory.createSocket(s, i, inaddr, j);
    }

    public Socket createSocket(String s, int i) throws IOException {
    	return factory.createSocket(s, i);
    }

    public String[] getDefaultCipherSuites() {
    	return factory.getDefaultCipherSuites();
    }

    public String[] getSupportedCipherSuites() {
    	return factory.getSupportedCipherSuites();
    }
}